import random
import time
import json
import time
import re
import os
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import threading
import datetime
from datetime import timedelta
from threading import Lock

#结束当前歌曲变量
result=''

#初始化参数
initFlag=False

"""队列歌单"""
list = []
head = 0
rear = 0
total_size = 11
lock=Lock()
def push(str):
    with lock:
        global head, rear
        if (rear + 1) % total_size == head:
            print('歌单列表已达上限')
            return
        elif str in list:  # 检查歌曲是否已存在
            print('歌曲已存在，不重复添加:', song)
            return
        else:
            print('新增歌曲:',str)
            list.append(str)
            rear = (rear + 1) % total_size
            print('当前歌单列表：')
            print(list)

def pop():
    with lock:
        global head
        if head == rear:
            return
        else:
            end=list.pop(0)
            head = (head + 1) % total_size
            return end


def time_to_seconds(time_str):
    """将 mm:ss 格式的时间转换为秒数"""
    minutes, seconds = map(int, time_str.split(':'))
    return minutes * 60 + seconds


"""QQ音乐网页端"""
class Wy:
    def __init__(self):
        # 初始化浏览器
        self.driver = webdriver.Edge()
        self.driver.get('https://y.qq.com/')
        self.driver.minimize_window()
        # 登录网易云音乐
        self.__login()

    def __login(self):
        #检查是否有qqMusicCookies文件
        file_path = "qqMusicCookies"
        if not os.path.exists(file_path):
            open(file_path, 'w').close()
            print(f"File '{file_path}'创建成功.")
        else:
            print('qqMusicCookies存在')

        #检测文件存在时长，避免cookies过期
        filemt1 = time.localtime(os.stat('qqMusicCookies').st_mtime)
        t1 = time.mktime(filemt1)
        print('文件最近一次更新日期：',time.strftime("%Y-%m-%d %H:%M:%S", filemt1))
        filemt2 = time.localtime()
        t2 = time.mktime(filemt2)
        print('当前系统日期:',time.strftime("%Y-%m-%d %H:%M:%S", filemt2))
        uni_time=datetime.timedelta(seconds=t2 - t1).days
        print(f'文件时间距当前系统时间差：{uni_time}天')
        if(uni_time>10+random.randint(1,4)):
            with open('qqMusicCookies','w') as file:
                file.write('')
            print('文件内容清空，重新获取cookies')
        else:
            if os.stat(file_path).st_size != 0:
                print('qqMusicCookies信息存在,直接登录')
                self.driver.quit()
                self.enterMusci()
                return

        """登录网易云音乐"""
        try:
            print('登录按钮')
            # 点击登录按钮
            WebDriverWait(self.driver, 10).until(
                EC.element_to_be_clickable((By.CLASS_NAME, 'top_login__link'))
            ).click()

            #转到qq框架
            try:
                WebDriverWait(self.driver, 10,0.5).until(
                    EC.frame_to_be_available_and_switch_to_it(
                        (By.CSS_SELECTOR, '.login-box-bd__ifr.login-box-bd__ifr_qq')
                    )
                )
                WebDriverWait(self.driver, 10,0.5).until(
                    EC.frame_to_be_available_and_switch_to_it(
                        (By.CSS_SELECTOR, '#ptlogin_iframe')
                    )
                )
                #登录
                WebDriverWait(self.driver, 10,0.5).until(
                    EC.element_to_be_clickable((By.CSS_SELECTOR, '.img_out_focus'))
                ).click()

            except Exception:
                print('未成功传到qq框架，尝试重新登录')
                self.__login()

            print('已转到qq登录iframe')

            #等待页面加载完成以获取完整的cookies
            time.sleep(3)

            #截取cookies信息到本地
            dictCookies=self.driver.get_cookies()
            jsonCookies=json.dumps(dictCookies)
            with open('qqMusicCookies', 'w') as f:
                f.write(jsonCookies)
            print('cookies保存成功！')

            self.driver.quit()
        except Exception as e:
            print("登录失败:", e)

        self.enterMusci()

    def enterMusci(self):
        # 转到歌曲搜索界面
        try:
            option=webdriver.EdgeOptions()
            option.add_argument('--headless')
            option.add_argument("--disable-gpu")
            option.add_argument("--window-size=1920,1080")
            #反爬措施
            user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 S```afari/537.36'
            option.add_argument(f'user-agent={user_agent}')
            referer='https://y.qq.com/n/yqq/song/001NmPTG1fVsUw.html'
            option.add_argument(f'Referer={referer}')
            self.driver = webdriver.Edge(options=option)
            self.driver.get('https://y.qq.com/')
            self.driver.minimize_window()
            # 从本地读取cookies
            with open('qqMusicCookies', 'r', encoding='utf8') as f:
                listCookies = json.loads(f.read())

            for cookie in listCookies:
                cookie_dict = {
                    'domain': '.y.qq.com',
                    'name': cookie.get('name'),
                    'value': cookie.get('value'),
                    'path': '/',
                    "expires": '',
                    'sameSite': cookie.get('sameSite'),
                    'secure': cookie.get('secure'),
                    'httpOnly': cookie.get('httpOnly')
                }
                self.driver.add_cookie(cookie_dict)
                print('cookied读取成功')

            # cookie更新后进入新网页
            self.driver.get('https://y.qq.com/n/ryqq/profile/like/song')
            self.driver.minimize_window()
            time.sleep(5)
            self.driver.minimize_window()
            self.driver.find_element(by=By.CLASS_NAME, value='icon_search.sprite').click()

        except Exception as e:
            print('转到搜索界面失败:', e)

    def startDg(self, name):
        global result

        self.driver.set_window_position(-2000, 0)  # 将窗口移出屏幕

        """搜索并播放歌曲"""
        # 清空搜索框并输入歌曲名称
        search_box = WebDriverWait(self.driver, 10).until(
            EC.presence_of_element_located((By.CSS_SELECTOR, '.mod_search .search_input__input'))
        )
        search_box.send_keys(Keys.CONTROL, 'a')
        search_box.send_keys(Keys.BACKSPACE)
        search_box.send_keys(name)
        search_box.send_keys(Keys.ENTER)
        #等待新的页面加载完成
        time.sleep(3)
        # 点击播放按钮
        WebDriverWait(self.driver, 10).until(
            EC.element_to_be_clickable((By.CSS_SELECTOR, '.mod_songlist_toolbar .mod_btn_green'))
        ).click()
        #获取窗口句柄
        list_hand=self.driver.window_handles
        self.driver.switch_to.window(list_hand[-1])
        #点击开始播放
        try:
            WebDriverWait(self.driver, 10).until(
                EC.element_to_be_clickable((By.CSS_SELECTOR, '.yqq-dialog-close-x'))
            ).click()
        except Exception:
            print('点击弹窗取消失败/不存在')

        play=False
        try:
            WebDriverWait(self.driver, 1).until(
                EC.element_to_be_clickable((By.CSS_SELECTOR, '.upload_btns__item'))
            ).click()
        except Exception:
            print('播放按钮未点击成功/不存在')
            play=True

        #元素定位失效则文本定位
        if(play):
            try:
                WebDriverWait(self.driver, 1).until(
                    EC.element_to_be_clickable((By.XPATH, '//*[text()="开始播放"]'))
                ).click()
            except Exception:
                print('文字定位播放按钮点击失败/不存在')

        try:
            self.driver.switch_to.alert.dismiss()
        except Exception:
            print('弹出框处理失败/不存在')

        try:
            WebDriverWait(self.driver, 1).until(
                EC.element_to_be_clickable((By.XPATH, '//*[text()="取消"]'))
            ).click()
        except Exception:
            print('文字定位取消定位失败/不存在')

        # 获取歌曲时长并等待播放完成
        duration = WebDriverWait(self.driver, 10).until(
            EC.presence_of_element_located((By.CSS_SELECTOR, '.songlist__time.songlist__time_delete'))
        ).text
        minutes, seconds = map(float, duration.split(':'))
        total_seconds = minutes * 60 + seconds
        print('播放时长(s):',total_seconds)

        # 获取开始时间
        startTime = time.time()
        #先前时间
        preCtime = None
        #用于判断当前时间是否倒退
        backTime='00:00'
        # 获取当前歌曲进度
        while True:
            current = WebDriverWait(self.driver, 10).until(
                EC.presence_of_element_located((By.CSS_SELECTOR, '.player_music__time'))
            ).text
            ctime = current.split('/')[0].strip()
            etime = current.split('/')[1].strip()
            if(result=='-1'):
                result=''
                break
            if (ctime == etime):
                break
            if(time_to_seconds(ctime)>60):
                backTime=ctime
            if(time_to_seconds(ctime)-time_to_seconds(backTime)<0):
                break
            #在10s后，没有播放，则点击播放按钮
            if time.time()-startTime>10 and ctime==preCtime:
                try:
                    startTime=time.time()
                    print('尝试重新播放')
                    self.driver.find_element(by=By.CSS_SELECTOR,value='.btn_big_play').click()
                    continue
                except Exception:
                    print('播放失败，退出')
                    break
            #每三秒获取一次进度
            time.sleep(3)
            preCtime=ctime

        print('播放结束')
        print('移除歌曲：',pop())
        print('当前歌单列表：')
        print(list)
        # 获取窗口句柄
        self.driver.close()
        list_hand = self.driver.window_handles
        self.driver.switch_to.window(list_hand[0])
        self.driver.minimize_window()


class Dm:
    def __init__(self):
        self.lock = threading.Lock()
        # 初始化
        print('请勿关闭打开的浏览器,请提前登录QQ客户端程序，运行过程中需要您手动复制直播间的完整域名')
        print('弹幕发送 歌曲结束 即可结束当前正在播放的歌曲')
        print('弹幕发送 点歌 歌名 即可进行点歌')
        print('初始化中...')
        self.wyy = Wy()
        self.driver = webdriver.Edge()
        #显示等待10s
        self.driver.implicitly_wait(10)
        print('请复制直播间完整域名')
        self.url=input()
        self.driver.get(self.url)
        self.driver.set_window_position(-2000, 0)  # 将窗口移出屏幕
        global initFlag
        initFlag = True
        time.sleep(10) #等待页面完全加载
        print('初始化完成')



    def reStart(self):
        """重启"""
        self.wyy = Wy()
        self.driver = webdriver.Edge()
        print('初始化中...')
        self.driver.get(self.url)
        self.driver.set_window_position(2000, 0)  # 将窗口移出屏幕
        time.sleep(10)
        print('初始化完成')

    """获取指定格式弹幕"""
    def getDm(self):
        with self.lock:
            self.driver.implicitly_wait(10)
            print('获取弹幕')
            #newdan = self.driver.find_elements(by=By.CSS_SELECTOR, value='.danmaku-item-right.v-middle.pointer.ts-dot-2.open-menu')[-1].text
            DMflag=True
            while(DMflag):
                try:
                    newdan = WebDriverWait(self.driver, 10).until(
                        EC.presence_of_all_elements_located(
                            (By.CSS_SELECTOR, '.danmaku-item-right.v-middle.pointer.ts-dot-2.open-menu'))
                    )[-1].text
                    str = newdan
                    DMflag=False
                except:
                    print('弹幕为空')

            startTime=time.time()
            while(True):
                self.driver.set_window_position(2000, 0)  # 将窗口移出屏幕
                endTime = time.time()
                #newdan=self.driver.find_elements(by=By.CSS_SELECTOR,value='.danmaku-item-right.v-middle.pointer.ts-dot-2.open-menu')[-1].text
                newdan=WebDriverWait(self.driver,10).until(
                    EC.presence_of_all_elements_located((By.CSS_SELECTOR,'.danmaku-item-right.v-middle.pointer.ts-dot-2.open-menu'))
                )[-1].text
                name = self.driver.find_elements(by=By.CSS_SELECTOR, value='.user-name.v-middle.pointer.open-menu')[-1].text
                if(str == newdan):
                    continue
                else:
                    str = newdan
                    print(name+" "+newdan)
                    #检测是否符合格式
                    try:
                        if(re.match('歌曲结束',str)):
                            global result
                            result='-1'
                            print('接收到歌曲结束指令')
                    except Exception:
                        pass
                    if(re.match('点歌\s(.+)',str)):
                        print("符合:",str)
                        push(''.join(str.split(' ')[1:]))
                #每100多秒刷新一次页面，避免弹幕过多影响性能
                timer=endTime-startTime
                if(int(timer)>100+random.randint(1,10)):
                    #记录刷新前最后一条弹幕
                    str=self.driver.find_elements(by=By.CSS_SELECTOR,value='.danmaku-item-right.v-middle.pointer.ts-dot-2.open-menu')[-1].text
                    self.driver.refresh()
                    print('页面刷新中:')
                    print('刷新前最后一条弹幕信息:', str)
                    time.sleep(10)
                    print('刷新完成')
                    #重置时间
                    startTime=time.time()
                    print('获取弹幕:')
                    continue


if __name__ == '__main__':
    dm=Dm()
    thread=threading.Thread(target=dm.getDm)
    thread.daemon=True
    thread.start()
    if initFlag:
        while True:
            song = len(list)
            if song:
                print('当前正在播放:',list[0])
                try:
                    dm.wyy.startDg(list[0])
                except Exception as e:
                    print('发生未知错误',e)
                    print('尝试重启..')
                    dm.wyy.driver.quit()
                    dm.wyy.enterMusci()

            time.sleep(1)
